package org.jvm.nativemethod.methods.java.lang;

import org.jvm.instruction.base.InstructionUtil;
import org.jvm.nativemethod.*;
import org.jvm.rtda.Object;
import org.jvm.rtda.heap.Klass;
import org.jvm.rtda.heap.classLoader.*;
import org.jvm.rtda.heap.classmember.*;
import org.jvm.rtda.heap.stringpool.StringPool;
import org.jvm.rtda.thread.Thread;
import org.jvm.rtda.thread.*;
import org.jvm.util.JvmUtil;

import java.util.Optional;

/**
 * @author 海燕
 * @date 2023/2/26
 */
public class ClassNativeMethod extends NativeMethodRegister {

    private static String jlClass = "java/lang/Class";

    public static void init() throws NoSuchMethodException {
        register(jlClass, "getPrimitiveClass", "(Ljava/lang/String;)Ljava/lang/Class;", ClassNativeMethod.class.getDeclaredMethod("getPrimitiveClass", Frame.class));
        register(jlClass, "getName0", "()Ljava/lang/String;", ClassNativeMethod.class.getDeclaredMethod("getName0", Frame.class));
        register(jlClass, "desiredAssertionStatus0", "(Ljava/lang/Class;)Z", ClassNativeMethod.class.getDeclaredMethod("desiredAssertionStatus0", Frame.class));
        register(jlClass, "isAssignableFrom", "(Ljava/lang/Class;)Z", ClassNativeMethod.class.getDeclaredMethod("isAssignableFrom", Frame.class));
        register(jlClass, "isInterface", "()Z", ClassNativeMethod.class.getDeclaredMethod("isInterface", Frame.class));
        register(jlClass, "isPrimitive", "()Z", ClassNativeMethod.class.getDeclaredMethod("isPrimitive", Frame.class));
        register(jlClass, "getDeclaredFields0", "(Z)[Ljava/lang/reflect/Field;", ClassNativeMethod.class.getDeclaredMethod("getDeclaredFields0", Frame.class));
        register(jlClass, "forName0", "(Ljava/lang/String;ZLjava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/Class;", ClassNativeMethod.class.getDeclaredMethod("forName0", Frame.class));
        register(jlClass, "getDeclaredConstructors0", "(Z)[Ljava/lang/reflect/Constructor;", ClassNativeMethod.class.getDeclaredMethod("getDeclaredConstructors0", Frame.class));
        register(jlClass, "getModifiers", "()I", ClassNativeMethod.class.getDeclaredMethod("getModifiers", Frame.class));
        register(jlClass, "getSuperclass", "()Ljava/lang/Class;", ClassNativeMethod.class.getDeclaredMethod("getSuperclass", Frame.class));
        register(jlClass, "getInterfaces0", "()[Ljava/lang/Class;", ClassNativeMethod.class.getDeclaredMethod("getInterfaces0", Frame.class));
        register(jlClass, "isArray", "()Z", ClassNativeMethod.class.getDeclaredMethod("isArray", Frame.class));
        register(jlClass, "getDeclaredMethods0", "(Z)[Ljava/lang/reflect/Method;", ClassNativeMethod.class.getDeclaredMethod("getDeclaredMethods0", Frame.class));
        register(jlClass, "getComponentType", "()Ljava/lang/Class;", ClassNativeMethod.class.getDeclaredMethod("getComponentType", Frame.class));
    }

    /**
     * java/lang/Class获取基本类型类的方法，入参是一个String为基本类名
     *
     * @param frame
     */
    public static void getPrimitiveClass(Frame frame) {
        Object classNameString = frame.getLocalVars().getRef(0);
        String primitiveClassName = JvmUtil.javaStringToJvmString(classNameString);
        Klass primitiveClass = KlassLoaderRegister.getBootKlassLoader().loadKlass(primitiveClassName);
        Object primitiveClassObject = primitiveClass.getjClass();
        frame.getOperandStack().pushRef(primitiveClassObject);
    }

    /**
     * java/lang/Class中方法，获取类名
     *
     * @param frame
     */
    public static void getName0(Frame frame) {
        //拿到类对象的引用
        Object thisRef = frame.getLocalVars().getRef(0);
        //获取类对象指向的类
        Klass refClass = (Klass) thisRef.getExtra();
        String javaName = refClass.getJavaName();
        //TODO 本地方法返回的String会被放入字符串池？这个结论确定吗？
        Object javaString = StringPool.jString(javaName);
        frame.getOperandStack().pushRef(javaString);
    }

    /**
     * 不讨论断言，默认给false
     *
     * @param frame
     */
    public static void desiredAssertionStatus0(Frame frame) {
        frame.getOperandStack().pushInt(0);
    }

    public static void isInterface(Frame frame) {
        Klass klass = (Klass) frame.getLocalVars().getRef(0).getExtra();
        frame.getOperandStack().pushBoolean(klass.isInterface());
    }

    public static void isPrimitive(Frame frame) {
        Klass klass = (Klass) frame.getLocalVars().getRef(0).getExtra();
        frame.getOperandStack().pushBoolean(klass.isPrimitive());
    }

    /**
     * 通过类名加载类
     *
     * @param frame
     */
    public static void forName0(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object javaClassNameRef = localVars.getRef(0);
        boolean initialize = localVars.getBoolean(1);
        //jLoader := vars.GetRef(2)
        String javaClassName = JvmUtil.javaStringToJvmString(javaClassNameRef).replace('.', '/');
        Klass jvmClass = KlassLoaderRegister.loadKlass(javaClassName, frame.getThread());
        Object javaClass = jvmClass.getjClass();
        //如果需要初始化的，需要先初始化该类
        if (initialize && !jvmClass.isInitStarted()) {
            //执行类初始化
            InstructionUtil.initClass(frame.getThread(), jvmClass);
        }
        frame.getOperandStack().pushRef(javaClass);
    }

    /**
     * 获取类修饰符
     *
     * @param frame
     */
    public static void getModifiers(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object thisRef = localVars.getThis();
        Klass klass = (Klass) thisRef.getExtra();
        int modifiers = klass.getAccessFlags();
        frame.getOperandStack().pushInt(modifiers);
    }

    public static void getSuperclass(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object thisRef = localVars.getThis();
        Klass klass = (Klass) thisRef.getExtra();
        Klass superClass = klass.getSuperClass();
        Object res = Optional.ofNullable(superClass).map(Klass::getjClass).orElse(null);
        frame.getOperandStack().pushRef(res);
    }

    /**
     * 返回类所实现的接口数组
     *
     * @param frame
     */
    public static void getInterfaces0(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object thisRef = localVars.getThis();
        Klass klass = (Klass) thisRef.getExtra();
        Klass[] interfaces = klass.getInterfaces();
        Object classArray = toClassArr(interfaces);
        frame.getOperandStack().pushRef(classArray);
    }

    private static Object toClassArr(Klass[] interfaces) {
        Klass arrayClass = KlassLoaderRegister.getBootKlassLoader().loadKlass("[Ljava/lang/Class;");
        Object array = arrayClass.newArray(interfaces.length);
        Object[] refs = array.refs();
        for (int i = 0; i < interfaces.length; i++) {
            refs[i] = interfaces[i].getjClass();
        }
        return array;
    }

    public static void isArray(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object thisRef = localVars.getThis();
        Klass klass = (Klass) thisRef.getExtra();
        frame.getOperandStack().pushBoolean(klass.isArray());
    }

    /**
     * 获取数组类的元素类
     *
     * @param frame
     */
    public static void getComponentType(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object thisRef = localVars.getThis();
        Klass klass = (Klass) thisRef.getExtra();
        Klass componentClass = klass.getComponentClass();
        frame.getOperandStack().pushRef(componentClass.getjClass());
    }

    public static void isAssignableFrom(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Klass thisClass = (Klass) localVars.getThis().getExtra();
        Klass thatClass = (Klass) localVars.getRef(1).getExtra();
        frame.getOperandStack().pushBoolean(thisClass.isAssignableFrom(thatClass));
    }

    private static final String _constructorConstructorDescriptor = "" +
            "(Ljava/lang/Class;" +
            "[Ljava/lang/Class;" +
            "[Ljava/lang/Class;" +
            "II" +
            "Ljava/lang/String;" +
            "[B[B)V";

    public static void getDeclaredConstructors0(Frame frame) {
        LocalVars vars = frame.getLocalVars();
        Object classObj = vars.getThis();
        boolean publicOnly = vars.getBoolean(1);
        Klass klass = (Klass) classObj.getExtra();
        Method[] constructors = klass.getConstructors(publicOnly);
        int constructorCount = constructors.length;
        BootKlassLoader classLoader = KlassLoaderRegister.getBootKlassLoader();
        Klass constructorClass = KlassLoaderRegister.getBootKlassLoader().loadKlass("java/lang/reflect/Constructor");
        Object fieldArr = constructorClass.arrayClass().newArray(constructorCount);
        OperandStack stack = frame.getOperandStack();
        stack.pushRef(fieldArr);
        if (constructorCount <= 0) {
            return;
        }
        Thread thread = frame.getThread();
        Object[] constructorObjs = fieldArr.refs();
        //获取java/lang/reflect/Constructor的构造器
        Method constructorInitMethod = constructorClass.getConstructor(_constructorConstructorDescriptor);

        for (int i = 0; i < constructors.length; i++) {
            Method constructor = constructors[i];
            //对于java/lang/reflect/Constructor的实例来说，extra字段指向构造器方法
            Object constructorObj = constructorClass.newObject();
            constructorObj.setExtra(constructor);
            constructorObjs[i] = constructorObj;
            OperandStack tempOperandStack = new OperandStack(9);
            tempOperandStack.pushRef(constructorObj);
            tempOperandStack.pushRef(classObj);
            tempOperandStack.pushRef(toClassArr(constructor.getParameterTypes(thread)));
            tempOperandStack.pushRef(toClassArr(constructor.getExceptionTypes(thread)));
            tempOperandStack.pushInt(constructor.getAccessFlags());
            tempOperandStack.pushInt(0);
            tempOperandStack.pushRef(getSignatureStr(constructor.signature()));
            tempOperandStack.pushRef(toByteArr(constructor.getAnnotationData()));
            tempOperandStack.pushRef(toByteArr(constructor.getParameterAnnotationData()));

            Frame tempFrame = NativeMethodUtil.newShimFrame(thread, tempOperandStack);
            //借用java构造器进行初始化
            InstructionUtil.invokeMethod(tempFrame, constructorInitMethod);
        }
    }

    private static Object toByteArr(byte[] annotationData) {
        Klass klass = KlassLoaderRegister.getBootKlassLoader().loadKlass("[B");
        Object byteArray = klass.newArray(annotationData.length);
        byteArray.setData(annotationData);
        return byteArray;
    }

    private static Object getSignatureStr(String signature) {
        return null;
    }

    final static String _fieldConstructorDescriptor = "" +
            "(Ljava/lang/Class;" +
            "Ljava/lang/String;" +
            "Ljava/lang/Class;" +
            "II" +
            "Ljava/lang/String;" +
            "[B)V";

    public static void getDeclaredFields0(Frame frame) {
        LocalVars vars = frame.getLocalVars();
        Object classObj = vars.getThis();
        boolean publicOnly = vars.getBoolean(1);

        Klass klass = (Klass) classObj.getExtra();
        Field[] fields = klass.getFields(publicOnly);
        int fieldCount = fields.length;

        BootKlassLoader classLoader = KlassLoaderRegister.getBootKlassLoader();
        Klass fieldClass = KlassLoaderRegister.getBootKlassLoader().loadKlass("java/lang/reflect/Field");
        Object fieldArr = fieldClass.arrayClass().newArray(fieldCount);

        OperandStack stack = frame.getOperandStack();
        stack.pushRef(fieldArr);

        if (fieldCount <= 0) {
            return;
        }
        Thread thread = frame.getThread();
        Object[] fieldObjs = fieldArr.refs();
        //获取java/lang/reflect/Constructor的构造器
        Method constructorInitMethod = fieldClass.getConstructor(_fieldConstructorDescriptor);
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            //对于java/lang/reflect/Constructor的实例来说，extra字段指向构造器方法
            Object jField = fieldClass.newObject();
            jField.setExtra(field);
            fieldObjs[i] = jField;
            OperandStack tempOperandStack = new OperandStack(8);
            tempOperandStack.pushRef(jField);
            tempOperandStack.pushRef(classObj);
            tempOperandStack.pushRef(StringPool.jString(field.getName()));
            tempOperandStack.pushRef(field.getType(thread).getjClass());
            tempOperandStack.pushInt(field.getAccessFlags());
            tempOperandStack.pushInt(field.getSlotId());
            tempOperandStack.pushRef(getSignatureStr(field.signature()));
            tempOperandStack.pushRef(toByteArr(field.getAnnotationData()));

            Frame tempFrame = NativeMethodUtil.newShimFrame(thread, tempOperandStack);
            //借用java构造器进行初始化
            InstructionUtil.invokeMethod(tempFrame, constructorInitMethod);
        }
    }

    final static String _methodConstructorDescriptor = "" +
            "(Ljava/lang/Class;" +
            "Ljava/lang/String;" +
            "[Ljava/lang/Class;" +
            "Ljava/lang/Class;" +
            "[Ljava/lang/Class;" +
            "II" +
            "Ljava/lang/String;" +
            "[B[B[B)V";


    public static void getDeclaredMethods0(Frame frame) {
        LocalVars vars = frame.getLocalVars();
        Object classObj = vars.getThis();
        boolean publicOnly = vars.getBoolean(1);

        Klass klass = (Klass) classObj.getExtra();
        Method[] methods = klass.getMethods(publicOnly);
        int methodCount = methods.length;
        Klass methodClass = KlassLoaderRegister.getBootKlassLoader().loadKlass("java/lang/reflect/Method");
        Object methodArr = methodClass.arrayClass().newArray(methodCount);

        OperandStack stack = frame.getOperandStack();
        stack.pushRef(methodArr);
        if (methodCount <= 0) {
            return;
        }
        Thread thread = frame.getThread();
        Object[] methodObjs = methodArr.refs();
        //获取java/lang/reflect/Constructor的构造器
        Method methodConstructor = methodClass.getConstructor(_methodConstructorDescriptor);

        for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];
            //对于java/lang/reflect/Constructor的实例来说，extra字段指向构造器方法
            Object methodObj = methodClass.newObject();
            methodObj.setExtra(method);
            methodObjs[i] = methodObj;

            OperandStack tempOperandStack = new OperandStack(12);
            tempOperandStack.pushRef(methodObj);
            tempOperandStack.pushRef(classObj);
            tempOperandStack.pushRef(StringPool.jString(method.getName()));
            tempOperandStack.pushRef(toClassArr(method.getParameterTypes(thread)));
            tempOperandStack.pushRef(method.getReturnType(thread).getjClass());
            tempOperandStack.pushRef(toClassArr(method.getExceptionTypes(thread)));
            tempOperandStack.pushInt(method.getAccessFlags());
            tempOperandStack.pushInt(0);
            tempOperandStack.pushRef(getSignatureStr(method.signature()));
            tempOperandStack.pushRef(toByteArr(method.getAnnotationData()));
            tempOperandStack.pushRef(toByteArr(method.getParameterAnnotationData()));
            tempOperandStack.pushRef(toByteArr(method.getAnnotationDefaultData()));

            Frame tempFrame = NativeMethodUtil.newShimFrame(thread, tempOperandStack);
            //借用java构造器进行初始化
            InstructionUtil.invokeMethod(tempFrame, methodConstructor);
        }
    }

}
